#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

#define TTY1	"/dev/tty11"
#define TTY2	"/dev/tty12"

#define BUFSIZE 1024

/*状态机状态枚举类型*/
enum
{
	STATE_R=1, 	 //读态
	STATE_W,	 //写态
	STATE_Ex,	 //异常终止态
	STATE_T		 //退出态
};

/*状态机结构体*/
struct fsm_st
{
	int state; //当前状态机的状态
	
    int sfd;//源文件描述符
    
    int dfd;//目标文件描述符
    
    int len;//读取长度
    
    int pos;//位置
   
    char * errstr; //报错信息
   
    char buf[BUFSIZE]; //buf缓冲区
};

/**************推动状态机****************/
static void fsm_driver(struct fsm_st*fsm)
{
    int ret;

    switch (fsm->state)
    {
    /*状态机读取*/
    case STATE_R:
    	/*读取到的源fd存储到buf中*/
        fsm->len = read(fsm->sfd,fsm->buf,BUFSIZE);
        /*如果读取0字节,退出状态机*/
        if (fsm->len == 0)
        fsm->state = STATE_T;
        /*如果读取<0字节,进行状态判断*/
        else if (fsm->len < 0)
        {
           /*如果读取<0字节,二次判断*/
            if (errno == EAGAIN)
                fsm->state =STATE_R;
            else
            {
            	/*宕机退出*/
                fsm->errstr = "read()";
                fsm->state =STATE_Ex;
            }
        }
        else
        /*都没有报错,说明读取正常,则开始状态机写入*/
        {
        /*******初始化写入的位置为0***************/
            fsm->pos = 0;
            fsm->state =STATE_W;
        }
        break;

   /*状态机写入*/
    case STATE_W:
        /*写入读取到的数据len*/
        ret = write(fsm->dfd,fsm->buf+fsm->pos,fsm->len);
		/*写入读取到的数据<0*/
        if(ret < 0)
        {
        	/*假的错误*/
            if (errno == EAGAIN)
            	/*再次进入写入*/
                fsm->state = STATE_W;
            else
            /*真正读取错误*/
            {
            	/*读取错误*/
                fsm->errstr = "read()";
                /*宕机退出*/
                fsm->state =STATE_Ex;
            }
        }
        else
      /***************坚持写够len个字节数***************/
        {
           /*******从pos的位置继续向下写入字节***************/
            fsm->pos += ret;
            fsm->len -= ret;
            /*如果写入完成*/
            if(fsm->len == 0)
                /*返回读态*/
                fsm->state = STATE_R;
            /*否则返回写态,继续写够len个字节*/
            else
                fsm->state = STATE_W;
        }
        break;

        /*宕机退出*/
    case STATE_Ex:
        perror(fsm->errstr);
        fsm->state = STATE_T;
        break;
        /*完整退出*/
    case STATE_T:
        /*do sth*/
        break;
    default:
        /*如果都不是以上任意一个状态,发送异常*/
        abort();
        break;
    }

}

static void relay(int fd1,int fd2)
{
	struct fsm_st fsm12,fsm21;  //定义结构体读左写右，读右写左
	
	/*首先保证文件是以非阻塞实现的*/
	int fd1_save = fcntl(fd1,F_GETFL);	//获取文件状态
	fcntl(fd1,F_SETFL,fd1_save|O_NONBLOCK);//追加文件描述符的状态为非阻塞
	
	int fd2_save = fcntl(fd2,F_GETFL);  //获取文件状态
	fcntl(fd2,F_SETFL,fd1_save|O_NONBLOCK); //追加文件描述符的状态为非阻塞
	/******************************/

	/*初始化状态机*/
	fsm12.state = STATE_R;
    fsm12.sfd = fd1;
    fsm12.dfd = fd2;
    
    fsm21.state = STATE_R;
    fsm21.sfd = fd2;
    fsm21.dfd = fd1;
	/**************/
	
	/*当不是退出态,推动状态机*/
    while (fsm12.state != STATE_T ||fsm21.state != STATE_T )
    {
        fsm_driver(&fsm12);
        fsm_driver(&fsm21);
    }
    /************************/

    /*恢复起始默认状态*/
    fcntl(fd1,F_SETFL,fd1_save);
    fcntl(fd2,F_SETFL,fd2_save);
    /******************/
}

int main()
{
	int fd1,fd2;
	/*模拟用户打开设备*/
	fd1 = open(TTY1,O_RDWR);
	if(fd1 < 0)
	{
		perror("open()");
		exit(1);
	}
	
	write(fd1,"TTY1\n",5);
	/*模拟用户打开设备,以非阻塞方式打开设备*/
	fd2 = open(TTY2,O_RDWR|O_NONBLOCK);
	if(fd2 < 0)
	{
		perror("open()");
		exit(1);
	}
	
	write(fd2,"TTY2\n",5);
	/*中继引擎函数*/
	relay(fd1,fd2);

	close(fd2);
	close(fd1);

	exit(0);
}
